package com.zs.usc.utils;


import com.zs.usc.enums.FormatTimePatterns;
import org.apache.commons.lang3.StringUtils;

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalUnit;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * LocalDateTime工具类
 */
public class TimeUtils {

    private TimeUtils() {

    }

    // 构造DateTimeFormatter
    private final static Map<String, DateTimeFormatter> mapDateTimeFormatter = new ConcurrentHashMap<>();

    /**
     * 获得日期格式化对象
     *
     * @param formatterPattern 格式化字符串
     * @return 日期格式化对象
     */
    public static DateTimeFormatter getDateTimeFormatter(String formatterPattern) {
        if (!mapDateTimeFormatter.containsKey(formatterPattern)) {
            mapDateTimeFormatter.put(formatterPattern, DateTimeFormatter.ofPattern(formatterPattern));
        }

        return mapDateTimeFormatter.get(formatterPattern);
    }

    /**
     * Date转换为LocalDateTime
     *
     * @param date date对象
     * @return localDateTime结果
     */
    public static LocalDateTime convertFromDate(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * LocalDateTime转换为Date
     *
     * @param time LocalDateTime对象
     * @return date结果
     */
    public static Date convertToDate(LocalDateTime time) {
        return Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 转换成LocalDateTime对象
     *
     * @param objIn 要转换的值
     * @return 转换之后的对象
     */
    public static LocalDateTime parseLocalDateTime(Object objIn) {
        if (null == objIn) {
            return null;
        }
        return LocalDateTime.parse(ConvertUtils.parseStr(objIn));
    }


    /**
     * 转换成LocalDateTime对象
     *
     * @param objIn     要转换的值
     * @param formatter 转换格式
     * @return 转换之后的对象
     */
    public static LocalDateTime parseLocalDateTime(Object objIn, FormatTimePatterns formatter) {
        if (null == objIn) {
            return null;
        }
        return parseLocalDateTime(objIn, formatter.getPattern());
    }

    /**
     * 转换成LocalDateTime对象
     *
     * @param objIn     要转换的值
     * @param formatStr 转换格式
     * @return 转换之后的对象
     */
    public static LocalDateTime parseLocalDateTime(Object objIn, String formatStr) {
        if (null == objIn) {
            return null;
        }
        return LocalDateTime.parse(ConvertUtils.parseStr(objIn), TimeUtils.getDateTimeFormatter(formatStr));
    }

    /**
     * 转换成LocalDate对象
     *
     * @param objIn     要转换的值
     * @param formatter 转换格式
     * @return 转换之后的对象
     */
    public static LocalDate parseLocalDate(Object objIn, FormatTimePatterns formatter) {
        if (null == objIn) {
            return null;
        }
        return parseLocalDate(objIn, formatter.getPattern());
    }

    /**
     * 转换成LocalDate对象
     *
     * @param objIn     要转换的值
     * @param formatStr 转换格式
     * @return 转换之后的对象
     */
    public static LocalDate parseLocalDate(Object objIn, String formatStr) {
        if (null == objIn) {
            return null;
        }
        return LocalDate.parse(ConvertUtils.parseStr(objIn), TimeUtils.getDateTimeFormatter(formatStr));
    }

    /**
     * 转换成LocalTime对象
     *
     * @param objIn     要转换的值
     * @param formatter 转换格式
     * @return 转换之后的对象
     */
    public static LocalTime parseLocalTime(Object objIn, FormatTimePatterns formatter) {
        if (null == objIn) {
            return null;
        }
        return parseLocalTime(objIn, formatter.getPattern());
    }

    /**
     * 转换成LocalTime对象
     *
     * @param objIn     要转换的值
     * @param formatStr 转换格式
     * @return 转换之后的对象
     */
    public static LocalTime parseLocalTime(Object objIn, String formatStr) {
        if (null == objIn) {
            return null;
        }
        return LocalTime.parse(ConvertUtils.parseStr(objIn), TimeUtils.getDateTimeFormatter(formatStr));
    }

    /**
     * 获取指定日期的毫秒
     *
     * @param time LocalDateTime对象
     * @return 毫秒值
     */
    public static Long getMilliByTime(LocalDateTime time) {
        return time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 获取指定日期的秒
     *
     * @param time LocalDateTime对象
     * @return 秒值
     */
    public static Long getSecondsByTime(LocalDateTime time) {
        return time.atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
    }

    /**
     * 获取指定时间的指定格式
     *
     * @param time    Time对象
     * @param pattern 格式化字符串
     * @return 格式化结果
     */
    public static String formatTime(TemporalAccessor time, String pattern) {
        return getDateTimeFormatter(pattern).format(time);
    }

    /**
     * 获取当前时间的指定格式
     *
     * @param pattern 格式化字符串
     * @return 格式化结果
     */
    public static String formatNow(String pattern) {
        return formatTime(LocalDateTime.now(), pattern);
    }

    /**
     * 按照转换格式枚举格式化时间
     *
     * @param time          Time对象
     * @param formatPattern 格式化枚举
     * @return 格式化结果
     */
    public static String formatTime(TemporalAccessor time, FormatTimePatterns formatPattern) {
        return TimeUtils.formatTime(time, formatPattern.getPattern());
    }

    /**
     * 日期加上一个数,根据field不同加不同值,field为ChronoUnit.*
     *
     * @param time   LocalDateTime对象
     * @param number 累加值
     * @param field  累加field
     * @return 累加之后的时间对象
     */
    public static LocalDateTime plus(LocalDateTime time, long number, TemporalUnit field) {
        return time.plus(number, field);
    }

    /**
     * 日期减去一个数,根据field不同减不同值,field参数为ChronoUnit.*
     *
     * @param time   LocalDateTime对象
     * @param number 累加值
     * @param field  累加field
     * @return 累加之后的时间对象
     */
    public static LocalDateTime minu(LocalDateTime time, long number, TemporalUnit field) {
        return time.minus(number, field);
    }

    /**
     * 获取两个日期的差  field参数为ChronoUnit.*
     *
     * @param firstTime  第一个时间对象
     * @param secondTime 第二个时间对象(大)
     * @param field      单位(年月日时分秒)
     * @return 比较结果
     */
    public static long betweenTwoTime(LocalDateTime firstTime, LocalDateTime secondTime, ChronoUnit field) {
        Period period = Period.between(LocalDate.from(firstTime), LocalDate.from(secondTime));
        if (field == ChronoUnit.YEARS) return period.getYears();
        if (field == ChronoUnit.MONTHS) return period.getYears() * 12 + period.getMonths();
        return field.between(firstTime, secondTime);
    }


    /**
     * 获取一天的开始时间，xxxx,xx,xx 00:00
     *
     * @param time LocalDateTime对象
     * @return 一天最早时间
     */
    public static LocalDateTime getDayStart(LocalDateTime time) {
        if (null == time) {
            return null;
        }

        return time.withHour(0)
                .withMinute(0)
                .withSecond(0)
                .withNano(0);
    }

    /**
     * 格式化日期
     *
     * @param date   日期对象
     * @param patern 格式字符串
     * @return 日期格式化之后的字符串
     */
    public static String getFormatDate(Date date, String patern) {
        return new SimpleDateFormat(patern).format(date);
    }

    /**
     * 转换为日期对象
     *
     * @param objIn 要转换的对象
     * @return 日期对象值，转换失败返回null
     */
    public static Date parseDate(Object objIn) {
        if (objIn == null) {
            return null;
        }
        return parseDate(objIn, null);
    }

    /**
     * 转换为日期对象
     *
     * @param objIn        要转换对象
     * @param defaultValue 转换失败的默认值
     * @return 转换之后的日期对象，如果转换失败则返回默认值
     */
    public static Date parseDate(Object objIn, Date defaultValue) {
        Date datRe = defaultValue;
        if (objIn == null) {
            return datRe;
        }
        try {
            if (objIn instanceof java.sql.Date) {
                return new Date(((java.sql.Date) objIn).getTime());
            } else if (objIn instanceof Timestamp) {
                return new Date(((Timestamp) objIn).getTime());
            } else if (objIn instanceof Date) {
                return (Date) objIn;
            }

            datRe = parseDate(ConvertUtils.parseStr(objIn));
        } catch (Exception ignored) {
        }
        return datRe;
    }

    /**
     * 使用"yyyy-MM-dd HH:mm:ss"格式化日期对象
     *
     * @param date 日期对象
     * @return 格式化之后的字符串
     */
    public static String getFormatPaternDate(Date date) {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
    }

    /**
     * 字符串转日期对象
     *
     * @param dateStr 日期字符串
     * @param format  日期格式
     * @return 转换之后的日期对象。format为空则采用"yyyy-MM-dd HH:mm:ss"转换；如果转换失败则返回null
     */
    public static Date parseDate(String dateStr, String format) {
        if (StringUtils.isEmpty(dateStr)) {
            return null;
        }
        try {
            if (StringUtils.isEmpty(format)) {
                format = "yyyy-MM-dd HH:mm:ss";
            }
            return new SimpleDateFormat(format).parse(dateStr);
        } catch (ParseException localParseException) {
        }
        return null;
    }

    /**
     * 采用"yyyy-MM-dd HH:mm:ss"格式将字符串转换成日期对象
     *
     * @param dateStr 日期字符串
     * @return 转换之后的日期对象
     */
    public static Date parseDate(String dateStr) {
        return parseDate(dateStr, "");
    }

    /**
     * 采用"yyyy-MM-dd"格式将字符串转换成日期对象
     *
     * @param dateStr 日期字符串
     * @return 转换之后的日期对象
     */
    public static Date parseDateOnly(String dateStr) {
        if (StringUtils.isEmpty(dateStr)) {
            return null;
        }
        try {
            return new SimpleDateFormat("yyyy-MM-dd").parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取一天的结束时间，xxxx,xx,xx 23:59:59.999999999
     *
     * @param time LocalDateTime对象
     * @return 一天最晚的时间
     */
    public static LocalDateTime getDayEnd(LocalDateTime time) {
        if (null == time) {
            return null;
        }

        return time.withHour(23)
                .withMinute(59)
                .withSecond(59)
                .withNano(999999999);
    }
}
